package com.peas.platform.common.core.configuration;

import cn.hutool.core.util.StrUtil;
import com.peas.platform.common.core.constant.Constants;
import com.peas.platform.common.core.dto.ResponseDto;
import com.peas.platform.common.core.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @author caizhh
 */
@ControllerAdvice(basePackages = Constants.BASE_PACKAGE)
@Slf4j
public class AlphaResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    /**
     * 对所有RestController的接口方法进行拦截
     */
	@Override
    @SuppressWarnings("NullableProblems")
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        AnnotatedElement element = returnType.getAnnotatedElement();
        List<Class<? extends Annotation>> list = new ArrayList<>();
        list.add(RequestMapping.class);
        list.add(GetMapping.class);
        list.add(PostMapping.class);
        list.add(PutMapping.class);
        list.add(DeleteMapping.class);
        boolean bool =  list.stream().anyMatch(anno -> anno.isAnnotation() && element.isAnnotationPresent(anno));
        Method method = returnType.getMethod();
        if (bool && method != null) {
            String clazz = method.getDeclaringClass().getName();
            String pkg = method.getDeclaringClass().getPackage().getName();
            boolean bool1 = StrUtil.startWith(pkg, Constants.BASE_PACKAGE);
            boolean bool2 = StrUtil.equals(clazz, "org.springframework.data.rest.webmvc.RepositoryEntityController");
            return bool1 || bool2;
        }
        return false;
    }

	@Override
    @SuppressWarnings("NullableProblems")
    public Object beforeBodyWrite(@Nullable Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        MediaType mediaType = response.getHeaders().getContentType();
        log.debug("mediaType:{}", mediaType);
        if (mediaType != null && mediaType.getType().equals(MediaType.APPLICATION_OCTET_STREAM_VALUE)) {
            return body;
        }
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        long curr = System.currentTimeMillis();
        Long start = RequestContextUtil.getInitTimestampMillis();
        long duration = start != null ? curr - start : 0;
        String requestId = RequestContextUtil.getCurrentRequestId();
        if (body instanceof ResponseDto) {
            ((ResponseDto<?>) body).setDuration(duration);
            ((ResponseDto<?>) body).setRequestId(requestId);
            return body;
        }
        if (body instanceof BaseException) {
            BaseException exception = (BaseException) body;
            ResponseDto<?> out = new ResponseDto<>(exception.getCode(), exception.getMessage(), null);
            out.setDuration(duration);
            out.setRequestId(requestId);
            return out;
        }
        if (body instanceof String) {
            String str = (String) body;
            ResponseDto<String> result = ResponseDto.getSuccessResponseDto(str);
            result.setDuration(duration);
            result.setRequestId(requestId);
            return result;
        }
        return ResponseDto.getSuccessResponseDto(body);
    }
}
